For Statements ============== .. raw:: html For statements is my favorite feature of Python. It's hard to really appreciate why it is so ground-breaking without knowing how other languages solve this problem. Syntax ------ .. code:: python for in : SUITE else: SUITE ```` is the same as for the assign statement. Parallel assignment is OK, and I use it often. We'll have examples that include this later. The simplest target is just a single variable name. ```` is an expression list. You can use star expressions or whatever you want, as long as the expr list evaluates to an iterable of any kind. The ``for`` statement first creates an iterator of the ````. Remember that it's OK to use an iterator, since the iterator of an iterator is just the iterator. Then, it calls ``next()`` on the iterator. If it raises ``StopIteration`` because it has exhausted all the items, then it will execute the ``else`` block, and continue with the next statement following the ``for`` statement. If there is a value, then it assigns the value to the ```` as if it were an assign statement. Then it runs the ``for`` block. If the ``for`` block executes a ``continue``, or it finishes, then the ``next()`` value is grabbed from the iterator, and it continues. If the ``for`` block executes a ``break`` statement, then the execution of the ``for`` block is interrupted, the ``else`` block bypassed, and execution continues with the next statement after the ``for`` statement. This can be rewritten as a while loop: .. code:: python i = iter() while True: try: = next(i) except StopIteration: break Understanding the Else Block ---------------------------- For a ``while`` loop, the ``else`` block is executed when the ``while`` condition evaluates to ``False``. It is the code that is run as long as the ``break`` statement is never run. For a ``for`` loop, the ``else`` block is executed when the ```` runs out of items. It is the code that is run as long as the ``break`` statement is never run. Although it is quite rare to see an ``else`` block for a ``while`` loop, it is actually pretty common to see an ``else`` block on a ``for`` loop. For instance, you might iterate across a sequence, looking for a particular value. If it is found, your code may ``break`` out of the loop, because it is pointless to look any further. In this case, the ``else`` block is run if it is *not* found. Example: Simple Iteration ------------------------- Typically, we use ``for`` loops to do something on each item in a sequence. In this case, we're going to print out values in a sequence. .. code:: python items = (1, 4.0, 1+2j, "seventeen") for i in items: print(i) Example: Two-deep Iteration --------------------------- It's not uncommon to have tuples of tuples. As long as they are simple nestings without arbitrary depth, we can use nested ``for`` loops to touch each item. In this case, we want to add two matrices. Note that we iterate across the indexes of each matrix. .. code:: python a = ( (0, 1, 4), (3, 2, 8), (1, 9, 7), ) b = ( (1, 2, 1), (0, 9, 2), (3, 4, 6), ) result = () for i in range(3): row = () for j in range(3): row = row + (a[i][j]+b[i][j],) result = result + (row,) It might be beneficial to take some time to understand the tuple addition in the last few lines. .. note:: Many programmers have a problem with short variable names such as ``i`` and ``j``. As long as these are used in the context of iteration of a sequence, such as in a ``for`` statement, I have never had an issue with it. ``enumerate()`` --------------- If you want to iterate across a sequence and keep track of which index you are at, then ``enumerate()`` is particularly powerful. ``enumerate()`` takes an iterable as an argument, and returns an iterator with tuple pairs. The first is the index of the item, and the second is the item. Let's play around with it a bit to understand how it works: .. code:: python >>> e = enumerate("Hello, World!") >>> next(e) (0, 'H') >>> next(e) (1, 'e') >>> next(e) (2, 'l') >>> next(e) (3, 'l') >>> next(e) (4, 'o') >>> next(e) (5, ',') >>> next(e) (6, ' ') >>> next(e) (7, 'W') Used in a ``for`` loop, this is particularly powerful: .. code:: python for i, char in enumerate("Hello, World!"): print("The character at", i, "is", char) You can also set the starting index with the second parameter. I use this if I have already cut up the sequence and want to get back to the original value. .. code:: python for i, char in enumerate("Hello, World!"[7:], 7): print("The character at", i, "is", char) Example: Finding an Item ------------------------ We already have the ``index()`` method to find where a particular element resides in a tuple, a string, or a bytes object. What if it isn't a simple compare we are after? In this example, we're looking for the third odd value in a sequence. .. code:: python values = (1, 2, 6, 5, 9, 11, 12) odds_seen = 0 for i, value in enumerate(values): if value % 2 == 1: odds_seen = odds_seen + 1 if odds_seen == 3: break Note that the variables ``i`` and ``value`` persist after the ``for`` loop has completed. (The only time variables don't persist is in an ``except`` block, and that's only because keeping an exception around will prevent garbage collection of the frames and stack from occurring.) Let's turn the above into a pure function. Remember, pure functions don't rely on global state and don't modify their arguments. .. code:: python def find_odds(values, num_odds=1): odds_seen = 0 for i, value in enumerate(values): if value % 2 == 1: odds_seen = odds_seen + 1 if odds_seen == 3: break return i We could've just as easily returned from inside the ``for`` loop rather than breaking. Using Variables After the ``For`` Statement ------------------------------------------- It may be tempting to always use the variables after a ``for`` loop, but this can be dangerous. If the sequence that is being iterated across is empty, then the variable will never get assigned. This leads many programmers to avoid using the variables at all. I think this is unfortunate, because there are cases where it can be very useful (see the "Finding an Item" example above.) Just be sure to either check that the sequence isn't empty, or catch and process the ``NameError`` exception if it was never used. Some people avoid using it out of ignorance of the way Python does things. For instance, in some languages, the variables are not accessible outside of the for loop body. Limitations of the For Loop --------------------------- The ``for`` loop is not a panacea. It is a convenience for particular cases where iterating across each item in a sequence and doing something is useful In particular, if you need to iterate across two loops in parallel, you can't use the ``for`` loop unless you are doing something like the matrix example above. IE, if you want to join two sequences together in sorted order, grabbing the lowest value from one of the sequences, the ``for`` loop won't help you.